package qiniu

import (
	"bytes"
	"context"
	"fmt"
	"net/url"
	"strings"
	"time"

	"github.com/qiniu/api.v7/v7/auth"
	"github.com/qiniu/api.v7/v7/storage"
)

type Qiniu struct {
	token           string
	expireTimeStamp uint64
	bucketName      string
	accessKey       string
	secretKey       string
	bucketURI       string
}

func (q *Qiniu) reloadQiniuToken() {
	// 这里延长token有效期到1天
	putPolicy := storage.PutPolicy{
		Scope:   q.bucketName,
		Expires: 86400,
	}
	mac := auth.New(q.accessKey, q.secretKey)
	q.token = putPolicy.UploadToken(mac)
	q.expireTimeStamp = putPolicy.Expires
}

// NewQiniu new qiniu client
func NewQiniu(bucketURI, accessKey, secretKey string) *Qiniu {
	tmp := strings.Split(bucketURI, "://")[1]
	bucketName := strings.Split(tmp, ".")[0]
	q := &Qiniu{
		bucketName: bucketName,
		accessKey:  accessKey,
		secretKey:  secretKey,
		bucketURI:  bucketURI,
	}
	q.reloadQiniuToken()
	return q
}

// UploadByBuffer 以二进制形式上传七牛云
func (q *Qiniu) UploadByBuffer(ctx context.Context, buf *bytes.Buffer, key string) (string, string, error) {
	now := uint64(time.Now().Unix()) - 120
	if q.expireTimeStamp < now {
		q.reloadQiniuToken()
	}
	cfg := storage.Config{
		Zone:          &storage.ZoneHuadong,
		UseHTTPS:      false,
		UseCdnDomains: false,
	}
	ret := storage.PutRet{}
	extra := storage.PutExtra{}
	uploader := storage.NewFormUploader(&cfg)
	data := buf.Bytes()
	dLen := int64(len(data))

	err := uploader.Put(ctx, &ret, q.token, key, bytes.NewReader(data), dLen, &extra)
	if err != nil {
		return "", "", err
	}
	link := fmt.Sprintf("%s/%s", q.bucketURI, url.QueryEscape(ret.Key))
	return link, ret.Hash, nil
}

// UploadFile 按文件形式上传
func (q *Qiniu) UploadFile(ctx context.Context, filePath string, fileName string) (string, string, error) {
	now := uint64(time.Now().Unix()) - 120
	if q.expireTimeStamp < now {
		// 过期需要重置token
		q.reloadQiniuToken()
	}
	cfg := storage.Config{}
	// 空间对应的机房
	cfg.Zone = &storage.ZoneHuadong
	// 是否使用https域名
	cfg.UseHTTPS = false
	// 上传是否使用CDN上传加速
	cfg.UseCdnDomains = false
	ret := storage.PutRet{}

	putExtra := storage.RputExtra{}
	uploader := storage.NewResumeUploader(&cfg)
	err := uploader.PutFile(
		ctx, &ret, q.token, fileName, filePath, &putExtra)
	if err != nil {
		return "", "", err
	} else {
		return fmt.Sprintf("%s/%s", q.bucketURI, url.QueryEscape(ret.Key)), ret.Hash, nil
	}
}

func (q *Qiniu) GetUploadToken(_ context.Context, bucket, overwriteKey string, expires int32) (string, int32) {
	if expires == 0 {
		expires = 7200
	}
	if bucket == "" {
		bucket = q.bucketURI
	}
	if overwriteKey != "" {
		bucket = fmt.Sprintf("%s:%s", bucket, overwriteKey)
	}
	mac := auth.New(q.accessKey, q.secretKey)
	putPolicy := storage.PutPolicy{
		Scope:   bucket,
		Expires: uint64(expires),
	}
	uToken := putPolicy.UploadToken(mac)
	return uToken, expires
}
